bitkeeper revision 1.1670 (42a1e657B8Qurf6JEQIMURchxp90FA)
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Sat, 4 Jun 2005 17:35:19 +0000 (17:35 +0000)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Sat, 4 Jun 2005 17:35:19 +0000 (17:35 +0000)
Interrupt-driven serial transmit for 8250/16550 UARTs.
Signed-off-by: Keir Fraser <keir@xensource.com>
xen/arch/x86/cdb.c
xen/arch/x86/domain.c
xen/arch/x86/traps.c
xen/drivers/char/console.c
xen/drivers/char/ns16550.c
xen/drivers/char/serial.c
xen/include/xen/console.h
xen/include/xen/serial.h

index ff9b6af07622402770cf453b8201723ecec80ae8..1c95245e2230532a5c9bf4bb3fcf83efbab624b8 100644 (file)
@@ -358,6 +358,7 @@ __trap_to_cdb(struct cpu_user_regs *regs)
        local_irq_save(flags);
 
        watchdog_disable();
+       console_start_sync();
 
        /* Shouldn't really do this, but otherwise we stop for no
           obvious reason, which is Bad */
@@ -383,9 +384,13 @@ __trap_to_cdb(struct cpu_user_regs *regs)
                        ASSERT(!local_irq_is_enabled());
                }
        }
+
+       console_end_sync();
        watchdog_enable();
        atomic_inc(&xendbg_running);
+
        local_irq_restore(flags);
+
        return 0;
 }
 
index 66732d899545511078e9ec1476ffea6766fa3c06..ae4099a008efd9d59e6a388950362c5ef8dcc8c1 100644 (file)
@@ -123,6 +123,9 @@ void machine_restart(char * __unused)
             safe_halt();
     }
 
+    watchdog_disable();
+    console_start_sync();
+
     local_irq_enable();
 
     /* Ensure we are the boot CPU. */
@@ -174,6 +177,7 @@ void __attribute__((noreturn)) __machine_halt(void *unused)
 void machine_halt(void)
 {
     watchdog_disable();
+    console_start_sync();
     smp_call_function(__machine_halt, NULL, 1, 0);
     __machine_halt(NULL);
 }
index 35940b0df8841472882a432b1edacb4baee14d20..9e9d82d13068b2013d7ac27ac0b054ba61c3ac03 100644 (file)
@@ -205,6 +205,7 @@ asmlinkage void fatal_trap(int trapnr, struct cpu_user_regs *regs)
     };
 
     watchdog_disable();
+    console_start_sync();
 
     show_registers(regs);
 
index 6f6234c8d0ca4e1803977da18782ff34ebad3329..7d7cafeaf54899a2b6a688726f853b953b82e394 100644 (file)
@@ -467,6 +467,16 @@ void console_force_lock(void)
     spin_lock(&console_lock);
 }
 
+void console_start_sync(void)
+{
+    serial_start_sync(sercon_handle);
+}
+
+void console_end_sync(void)
+{
+    serial_end_sync(sercon_handle);
+}
+
 void console_putc(char c)
 {
     serial_putc(sercon_handle, c);
index ea094c843cb0313e4852379866b5eeee268a9cc7..f52ea04ac2258ce5c4b36ec44c7940a8c9ea85de 100644 (file)
@@ -101,16 +101,24 @@ static void ns_write_reg(struct ns16550 *uart, int reg, char c)
 static void ns16550_interrupt(
     int irq, void *dev_id, struct cpu_user_regs *regs)
 {
-    serial_rx_interrupt(dev_id, regs);
+    struct serial_port *port = dev_id;
+    struct ns16550 *uart = port->uart;
+
+    if ( (ns_read_reg(uart, IIR) & 7) == 2 )
+        serial_tx_interrupt(port, regs);
+    else
+        serial_rx_interrupt(port, regs);
 }
 
-static void ns16550_putc(struct serial_port *port, char c)
+static int ns16550_tx_empty(struct serial_port *port)
 {
     struct ns16550 *uart = port->uart;
+    return !!(ns_read_reg(uart, LSR) & LSR_THRE);
+}
 
-    while ( !(ns_read_reg(uart, LSR) & LSR_THRE) )
-        cpu_relax();
-
+static void ns16550_putc(struct serial_port *port, char c)
+{
+    struct ns16550 *uart = port->uart;
     ns_write_reg(uart, THR, c);
 }
 
@@ -150,6 +158,10 @@ static void ns16550_init_preirq(struct serial_port *port)
 
     /* Enable and clear the FIFOs. Set a large trigger threshold. */
     ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14);
+
+    /* Check this really is a 16550+. Otherwise we have no FIFOs. */
+    if ( (ns_read_reg(uart, IIR) & 0xc0) == 0xc0 )
+        port->tx_fifo_size = 16;
 }
 
 static void ns16550_init_postirq(struct serial_port *port)
@@ -157,20 +169,19 @@ static void ns16550_init_postirq(struct serial_port *port)
     struct ns16550 *uart = port->uart;
     int rc;
 
+    serial_async_transmit(port);
+
     uart->irqaction.handler = ns16550_interrupt;
     uart->irqaction.name    = "ns16550";
     uart->irqaction.dev_id  = port;
     if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )
         printk("ERROR: Failed to allocate na16550 IRQ %d\n", uart->irq);
 
-    /* For sanity, clear the receive FIFO. */
-    ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_TRG14);
-
     /* Master interrupt enable; also keep DTR/RTS asserted. */
     ns_write_reg(uart, MCR, MCR_OUT2 | MCR_DTR | MCR_RTS);
 
-    /* Enable receive interrupts. */
-    ns_write_reg(uart, IER, IER_ERDAI);
+    /* Enable receive and transmit interrupts. */
+    ns_write_reg(uart, IER, IER_ERDAI | IER_ETHREI);
 }
 
 #ifdef CONFIG_X86
@@ -188,6 +199,7 @@ static struct uart_driver ns16550_driver = {
     .init_preirq  = ns16550_init_preirq,
     .init_postirq = ns16550_init_postirq,
     .endboot      = ns16550_endboot,
+    .tx_empty     = ns16550_tx_empty,
     .putc         = ns16550_putc,
     .getc         = ns16550_getc
 };
index e8d75a343ba42cf5574f187a31de7579bd2a5608..2116d8ef089ee52e4edd66dd091d62f492eae0c9 100644 (file)
@@ -42,8 +42,8 @@ void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
             fn = port->rx_hi;
         else if ( !(c & 0x80) && (port->rx_lo != NULL) )
             fn = port->rx_lo;
-        else if ( (port->rxbufp - port->rxbufc) != RXBUFSZ )
-            port->rxbuf[MASK_RXBUF_IDX(port->rxbufp++)] = c;            
+        else if ( (port->rxbufp - port->rxbufc) != SERIAL_RXBUFSZ )
+            port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufp++)] = c;            
 
         spin_unlock_irqrestore(&port->lock, flags);
 
@@ -56,6 +56,71 @@ void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
     spin_unlock_irqrestore(&port->lock, flags);
 }
 
+void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
+{
+    int i;
+    unsigned long flags;
+
+    BUG_ON(!port->driver);
+    BUG_ON(!port->driver->tx_empty);
+    BUG_ON(!port->driver->putc);
+
+    spin_lock_irqsave(&port->lock, flags);
+
+    for ( i = 0; i < port->tx_fifo_size; i++ )
+    {
+        if ( port->txbufc == port->txbufp )
+            break;
+        port->driver->putc(
+            port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
+    }
+
+    spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void __serial_putc(struct serial_port *port, char c)
+{
+    int i;
+
+    if ( (port->txbuf != NULL) && !port->sync )
+    {
+        /* Interrupt-driven (asynchronous) transmitter. */
+        if ( (port->txbufp - port->txbufc) == SERIAL_TXBUFSZ )
+        {
+            /* Buffer is full: we spin, but could alternatively drop chars. */
+            while ( !port->driver->tx_empty(port) )
+                cpu_relax();
+            for ( i = 0; i < port->tx_fifo_size; i++ )
+                port->driver->putc(
+                    port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
+            port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c;
+        }
+        else if ( ((port->txbufp - port->txbufc) == 0) &&
+                  port->driver->tx_empty(port) )
+        {
+            /* Buffer and UART FIFO are both empty. */
+            port->driver->putc(port, c);
+        }
+        else
+        {
+            /* Normal case: buffer the character. */
+            port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c;
+        }
+    }
+    else if ( port->driver->tx_empty )
+    {
+        /* Synchronous finite-capacity transmitter. */
+        while ( !port->driver->tx_empty(port) )
+            cpu_relax();
+        port->driver->putc(port, c);
+    }
+    else
+    {
+        /* Simple synchronous transmitter. */
+        port->driver->putc(port, c);
+    }
+}
+
 void serial_putc(int handle, char c)
 {
     struct serial_port *port = &com[handle & SERHND_IDX];
@@ -67,14 +132,14 @@ void serial_putc(int handle, char c)
     spin_lock_irqsave(&port->lock, flags);
 
     if ( (c == '\n') && (handle & SERHND_COOKED) )
-        port->driver->putc(port, '\r');
+        __serial_putc(port, '\r');
 
     if ( handle & SERHND_HI )
         c |= 0x80;
     else if ( handle & SERHND_LO )
         c &= 0x7f;
 
-    port->driver->putc(port, c);
+    __serial_putc(port, c);
 
     spin_unlock_irqrestore(&port->lock, flags);
 }
@@ -101,7 +166,7 @@ char serial_getc(int handle)
             
             if ( port->rxbufp != port->rxbufc )
             {
-                c = port->rxbuf[MASK_RXBUF_IDX(port->rxbufc++)];
+                c = port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufc++)];
                 break;
             }
             
@@ -201,6 +266,46 @@ void serial_force_unlock(int handle)
     struct serial_port *port = &com[handle & SERHND_IDX];
     if ( handle != -1 )
         port->lock = SPIN_LOCK_UNLOCKED;
+    serial_start_sync(handle);
+}
+
+void serial_start_sync(int handle)
+{
+    struct serial_port *port = &com[handle & SERHND_IDX];
+    unsigned long flags;
+
+    if ( handle == -1 )
+        return;
+    
+    spin_lock_irqsave(&port->lock, flags);
+
+    if ( port->sync++ == 0 )
+    {
+        while ( (port->txbufp - port->txbufc) != 0 )
+        {
+            while ( !port->driver->tx_empty(port) )
+                cpu_relax();
+            port->driver->putc(
+                port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
+        }
+    }
+
+    spin_unlock_irqrestore(&port->lock, flags);
+}
+
+void serial_end_sync(int handle)
+{
+    struct serial_port *port = &com[handle & SERHND_IDX];
+    unsigned long flags;
+
+    if ( handle == -1 )
+        return;
+    
+    spin_lock_irqsave(&port->lock, flags);
+
+    port->sync--;
+
+    spin_unlock_irqrestore(&port->lock, flags);
 }
 
 void serial_init_preirq(void)
@@ -229,8 +334,19 @@ void serial_endboot(void)
 
 void serial_register_uart(int idx, struct uart_driver *driver, void *uart)
 {
+    /* Store UART-specific info. */
     com[idx].driver = driver;
     com[idx].uart   = uart;
+
+    /* Default is no transmit FIFO. */
+    com[idx].tx_fifo_size = 1;
+}
+
+void serial_async_transmit(struct serial_port *port)
+{
+    BUG_ON(!port->driver->tx_empty);
+    if ( !port->txbuf )
+        port->txbuf = (char *)alloc_xenheap_pages(get_order(SERIAL_TXBUFSZ));
 }
 
 /*
index 024840c0addec83513bed6876bad1feb7bbdca68..4295a41de6b775cf09d2a52b565a31f608ca5922 100644 (file)
@@ -22,4 +22,7 @@ void console_endboot(int disable_vga);
 void console_force_unlock(void);
 void console_force_lock(void);
 
+void console_start_sync(void);
+void console_end_sync(void);
+
 #endif /* __CONSOLE_H__ */
index 73daa385f0bbe1029b9d2e295b874d05a15a2085..9a1dd60cf4ad8acf173755cb8f3f1df2fec7209f 100644 (file)
@@ -16,8 +16,12 @@ typedef void (*serial_rx_fn)(char, struct cpu_user_regs *);
 void serial_set_rx_handler(int handle, serial_rx_fn fn);
 
 /* Number of characters we buffer for a polling receiver. */
-#define RXBUFSZ 32
-#define MASK_RXBUF_IDX(_i) ((_i)&(RXBUFSZ-1))
+#define SERIAL_RXBUFSZ 32
+#define MASK_SERIAL_RXBUF_IDX(_i) ((_i)&(SERIAL_RXBUFSZ-1))
+
+/* Number of characters we buffer for an interrupt-driven transmitter. */
+#define SERIAL_TXBUFSZ 16384
+#define MASK_SERIAL_TXBUF_IDX(_i) ((_i)&(SERIAL_TXBUFSZ-1))
 
 struct uart_driver;
 
@@ -25,10 +29,17 @@ struct serial_port {
     /* Uart-driver parameters. */
     struct uart_driver *driver;
     void               *uart;
+    /* Number of characters the port can hold for transmit. */
+    int                 tx_fifo_size;
+    /* Transmit data buffer (interrupt-driven uart). */
+    char               *txbuf;
+    unsigned int        txbufp, txbufc;
+    /* Force synchronous transmit. */
+    int                 sync;
     /* Receiver callback functions (asynchronous receivers). */
     serial_rx_fn        rx_lo, rx_hi, rx;
     /* Receive data buffer (polling receivers). */
-    char                rxbuf[RXBUFSZ];
+    char                rxbuf[SERIAL_RXBUFSZ];
     unsigned int        rxbufp, rxbufc;
     /* Serial I/O is concurrency-safe. */
     spinlock_t          lock;
@@ -40,9 +51,11 @@ struct uart_driver {
     void (*init_postirq)(struct serial_port *);
     /* Hook to clean up after Xen bootstrap (before domain 0 runs). */
     void (*endboot)(struct serial_port *);
-    /* Put a char onto the serial line. */
+    /* Transmit FIFO ready to receive up to @tx_fifo_size characters? */
+    int  (*tx_empty)(struct serial_port *);
+    /* Put a character onto the serial line. */
     void (*putc)(struct serial_port *, char);
-    /* Get a char from the serial line: returns FALSE if no char available. */
+    /* Get a character from the serial line: returns 0 if none available. */
     int  (*getc)(struct serial_port *, char *);
 };
 
@@ -76,13 +89,23 @@ void serial_puts(int handle, const char *s);
 char serial_getc(int handle);
 
 /* Forcibly prevent serial lockup when the system is in a bad way. */
+/* (NB. This also forces an implicit serial_start_sync()). */
 void serial_force_unlock(int handle);
 
+/* Start/end a synchronous region (temporarily disable interrupt-driven tx). */
+void serial_start_sync(int handle);
+void serial_end_sync(int handle);
+
+/*
+ * Initialisation and helper functions for uart drivers.
+ */
 /* Register a uart on serial port @idx (e.g., @idx==0 is COM1). */
 void serial_register_uart(int idx, struct uart_driver *driver, void *uart);
-
-/* Driver helper function: process receive work in interrupt context. */
+/* Place the serial port into asynchronous transmit mode. */
+void serial_async_transmit(struct serial_port *port);
+/* Process work in interrupt context. */
 void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs);
+void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs);
 
 /*
  * Initialisers for individual uart drivers.